package com.hzh.common.httpclient;

import java.io.File;
import java.nio.charset.Charset;
import java.nio.charset.CodingErrorAction;
import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;
import java.security.cert.X509Certificate;

import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;

import org.apache.commons.lang.StringUtils;
import org.apache.http.Consts;
import org.apache.http.Header;
import org.apache.http.HttpEntity;
import org.apache.http.HttpException;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.methods.HttpRequestBase;
import org.apache.http.client.utils.URLEncodedUtils;
import org.apache.http.config.ConnectionConfig;
import org.apache.http.config.MessageConstraints;
import org.apache.http.config.Registry;
import org.apache.http.config.RegistryBuilder;
import org.apache.http.config.SocketConfig;
import org.apache.http.conn.socket.ConnectionSocketFactory;
import org.apache.http.conn.socket.PlainConnectionSocketFactory;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.conn.ssl.SSLContexts;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.mime.HttpMultipartMode;
import org.apache.http.entity.mime.MultipartEntityBuilder;
import org.apache.http.entity.mime.content.FileBody;
import org.apache.http.entity.mime.content.StringBody;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.apache.http.util.CharsetUtils;
import org.apache.http.util.EntityUtils;

/**
 * HttpClient 访问工具类 注：需要 HttpClient 版本 4.3
 * 
 * @author 黄展豪
 * 
 */
public class HttpProtocolHandler {

	private static String DEFAULT_CHARSET = "UTF-8";

	/** 连接超时时间，由bean factory设置，缺省为8秒钟 */
	private int defaultConnectionTimeout = 180000;

	/** 回应超时时间, 由bean factory设置，缺省为30秒钟 */
	private int defaultSoTimeout = 180000;

	/** 闲置连接超时时间, 由bean factory设置，缺省为60秒钟 */
	private int defaultIdleConnTimeout = 60000;

	private int defaultMaxConnPerHost = 40;

	private int defaultMaxTotalConn = 80;

	/** 默认等待HttpConnectionManager返回连接超时（只有在达到最大连接数时起作用）：1秒 */
	private static final long defaultHttpConnectionManagerTimeout = 3 * 1000;

	/**
	 * HTTP连接管理器，该连接管理器必须是线程安全的.
	 */
	private PoolingHttpClientConnectionManager connectionManager;

	CloseableHttpClient httpclient;

	private static HttpProtocolHandler httpProtocolHandler = new HttpProtocolHandler();

	/**
	 * 工厂方法
	 * 
	 * @return
	 */
	public static HttpProtocolHandler getInstance() {
		return httpProtocolHandler;
	}

	/**
	 * 私有的构造方法
	 */
	private HttpProtocolHandler() {
		try {
			SSLContext sslContext = SSLContexts.custom().useTLS().build();
			sslContext.init(null, new TrustManager[] { new X509TrustManager() {

				public X509Certificate[] getAcceptedIssuers() {
					return null;
				}

				public void checkClientTrusted(X509Certificate[] certs,
						String authType) {
				}

				public void checkServerTrusted(X509Certificate[] certs,
						String authType) {
				}
			} }, null);
			Registry<ConnectionSocketFactory> socketFactoryRegistry = RegistryBuilder
					.<ConnectionSocketFactory> create()
					.register("http", PlainConnectionSocketFactory.INSTANCE)
					.register("https",
							new SSLConnectionSocketFactory(sslContext)).build();

			connectionManager = new PoolingHttpClientConnectionManager(
					socketFactoryRegistry);
			httpclient = HttpClients.custom()
					.setConnectionManager(connectionManager).build();
			// Create socket configuration
			SocketConfig socketConfig = SocketConfig.custom()
					.setTcpNoDelay(true).build();
			connectionManager.setDefaultSocketConfig(socketConfig);
			// Create message constraints
			MessageConstraints messageConstraints = MessageConstraints.custom()
					.setMaxHeaderCount(200).setMaxLineLength(2000).build();
			// Create connection configuration
			ConnectionConfig connectionConfig = ConnectionConfig.custom()
					.setMalformedInputAction(CodingErrorAction.IGNORE)
					.setUnmappableInputAction(CodingErrorAction.IGNORE)
					.setCharset(Charset.forName(DEFAULT_CHARSET))
					.setMessageConstraints(messageConstraints).build();
			connectionManager.setDefaultConnectionConfig(connectionConfig);
			connectionManager.setMaxTotal(defaultMaxTotalConn);
			connectionManager.setDefaultMaxPerRoute(defaultMaxConnPerHost);
		} catch (KeyManagementException e) {
			// logger.error("KeyManagementException", e);
		} catch (NoSuchAlgorithmException e) {
			// logger.error("NoSuchAlgorithmException", e);
		}
	}

	/**
	 * 执行Http请求
	 * 
	 * @param request
	 *            请求数据
	 * @param strParaFileName
	 *            文件名的参数名
	 * @param strFilePath
	 *            文件路径
	 * @param headers 
	 * @return
	 * @throws HttpException
	 *             , IOException
	 */
	public HttpResponse execute(HttpRequest request, String strParaFileName,
			String strFilePath, Header[] headers) throws Exception {

		// 设置连接超时
		int connectionTimeout = defaultConnectionTimeout;
		if (request.getConnectionTimeout() > 0) {
			connectionTimeout = request.getConnectionTimeout();
		}

		// 设置回应超时
		int soTimeout = defaultSoTimeout;
		if (request.getTimeout() > 0) {
			soTimeout = request.getTimeout();
		}

		RequestConfig requestConfig = RequestConfig.custom()
				.setSocketTimeout(soTimeout)
				.setConnectTimeout(connectionTimeout)
				.setConnectionRequestTimeout(connectionTimeout).build();

		String charset = request.getCharset();
		charset = charset == null ? DEFAULT_CHARSET : charset;
		HttpRequestBase hrb = null;

		// get模式且不带上传文件
		if (request.getMethod().equals(HttpRequest.METHOD_GET)) {

			// get 方法请求
			String url = request.getUrl();
			if (request.getParameters() != null) {
				if (!url.contains("?")) {
					url += "?"
							+ URLEncodedUtils.format(request.getParameters(),
									CharsetUtils.get(charset));
				} else {
					url += "&"
							+ URLEncodedUtils.format(request.getParameters(),
									CharsetUtils.get(charset));
				}
			}
			hrb = new HttpGet(url);

		} else if (StringUtils.isEmpty(strParaFileName)
				|| StringUtils.isEmpty(strFilePath)) {
			// post模式且不带上传文件
			hrb = new HttpPost(request.getUrl());

			if (request.getParameters() != null
					&& request.getParameters().size() > 0) {
				((HttpPost) hrb).setEntity(new UrlEncodedFormEntity(request
						.getParameters(), charset));
			}
			hrb.addHeader("Content-Type",
					"application/x-www-form-urlencoded; text/html; charset="
							+ charset);
		} else {
			// post模式且带上传文件
			hrb = new HttpPost(request.getUrl());

			// 处理文件，把文件转换成流对象FileBody
			File file = new File(strFilePath);
			FileBody bin = new FileBody(file);
			StringBody uploadFileName = new StringBody(strParaFileName,
					ContentType.create("text/plain", Consts.UTF_8));

			// 以浏览器兼容模式运行，防止文件名乱码。
			MultipartEntityBuilder reqEntity = MultipartEntityBuilder.create()
					.setMode(HttpMultipartMode.BROWSER_COMPATIBLE)
					.addPart("uploadFile", bin)// uploadFile对应服务端类的同名属性<File类型>
					.addPart("uploadFileName", uploadFileName)// uploadFileName对应服务端类的同名属性<String类型>
					.setCharset(CharsetUtils.get(charset));

			for (int i = 0; i < request.getParameters().size(); i++) {
				StringBody stringBody = new StringBody(request.getParameters()
						.get(i).getValue(), ContentType.create("text/plain",
						charset));
				reqEntity.addPart(request.getParameters().get(i).getName(),
						stringBody);
			}

			// 设置请求体
			((HttpPost) hrb).setEntity(reqEntity.build());
		}
		
		if(headers != null) {
			hrb.setHeaders(headers);			
		}
		// 设置Http Header中的User-Agent属性
		hrb.addHeader("User-Agent", "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/35.0.1916.153 Safari/537.36 SE 2.X MetaSr 1.0");
		
		// 设置请求参数
		hrb.setConfig(requestConfig);

		HttpResponse response = new HttpResponse();

		try {
			CloseableHttpResponse hResponse = httpclient.execute(hrb);
			// 获取响应对象
			HttpEntity resEntity = hResponse.getEntity();
			if (request.getResultType().equals(HttpResultType.STRING)) {
				response.setStringResult(EntityUtils.toString(resEntity,
						charset));
			} else if (request.getResultType().equals(HttpResultType.BYTES)) {
				response.setByteResult(EntityUtils.toByteArray(resEntity));
			}
			response.setResponseHeaders(hResponse.getAllHeaders());
		} catch (Exception ex) {
			throw ex;
		} finally {
			hrb.releaseConnection();
		}
		return response;
	}

	
}
